package com.wugui.datax.executor.service.command;

import com.wugui.datatx.core.biz.model.TriggerParam;
import com.wugui.datatx.core.enums.IncrementTypeEnum;
import com.wugui.datatx.core.log.JobLogger;
import com.wugui.datatx.core.util.Constants;
import com.wugui.datatx.core.util.DateUtil;
import com.wugui.datax.executor.service.jobhandler.CustomTimeDomain;
import com.wugui.datax.executor.service.jobhandler.TimeDomainVal;
import com.wugui.datax.executor.util.SystemUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.wugui.datatx.core.util.Constants.SPLIT_COMMA;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.DEFAULT_DATAX_PY;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.JVM_CM;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.PARAMS_CM;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.PARAMS_CM_V_PT;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.PERCENT;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.SPLIT_SPACE;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.TRANSFORM_QUOTES;
import static com.wugui.datax.executor.service.jobhandler.DataXConstant.TRANSFORM_SPLIT_SPACE;

/**
 * DataX command build
 *
 * @author jingwk 2020-06-07
 */
public class BuildCommand {

    //动态变量
    private static final Pattern VAR_PAT = Pattern.compile("-D(\\w+)\\s*=\\s*([^\\s]+)");
    //时间单位
    private static final Pattern TIME_PAT = Pattern.compile("(SYS_STD_START_TIME|SYS_STD_END_TIME|SYS_STD_DATE)([-|+][0-9]+)([D|H|M|S|d|h|m|s]+)");
    protected static Pattern VAL_PAT = Pattern.compile("(-?\\d+)([D|H|M|S])");

    /**
     * DataX command build
     * 0
     *
     * @param tgParam
     * @param tmpFilePath
     * @param dataXPyPath
     * @return
     */
    public static String[] buildDataXExecutorCmd(TriggerParam tgParam, String tmpFilePath, String dataXPyPath) {
        // command process
        //"--loglevel=debug"
        List<String> cmdArr = new ArrayList<>();
        cmdArr.add("python");
        String dataXHomePath = SystemUtils.getDataXHomePath();
        if (StringUtils.isNotEmpty(dataXHomePath)) {
            dataXPyPath = dataXHomePath.contains("bin") ? dataXHomePath + DEFAULT_DATAX_PY : dataXHomePath + "bin" + File.separator + DEFAULT_DATAX_PY;
        }
        cmdArr.add(dataXPyPath);
        String doc = buildDataXParam(tgParam);
        if (StringUtils.isNotBlank(doc)) {
            cmdArr.add(doc.replaceAll(SPLIT_SPACE, TRANSFORM_SPLIT_SPACE));
        }
        cmdArr.add(tmpFilePath);
        return cmdArr.toArray(new String[cmdArr.size()]);
    }

    private static String buildDataXParam(TriggerParam tgParam) {
        StringBuilder doc = new StringBuilder();
        String jvmParam = StringUtils.isNotBlank(tgParam.getJvmParam()) ? tgParam.getJvmParam().trim() : tgParam.getJvmParam();
        String partitionStr = tgParam.getPartitionInfo();
        if (StringUtils.isNotBlank(jvmParam)) {
            doc.append(JVM_CM).append(TRANSFORM_QUOTES).append(jvmParam).append(TRANSFORM_QUOTES);
        }
        JobLogger.log("------------------任务触发时间:" + tgParam.getTriggerTime());

        Integer incrementType = tgParam.getIncrementType();
        String replaceParam = StringUtils.isNotBlank(tgParam.getReplaceParam()) ? tgParam.getReplaceParam().trim() : null;
        String replaceParamType = tgParam.getReplaceParamType();

        if (incrementType != null && replaceParam != null) {
            if (IncrementTypeEnum.TIME.getCode() == incrementType) {
                if (doc.length() > 0) doc.append(SPLIT_SPACE);
                if (StringUtils.isBlank(replaceParamType) || replaceParamType.equals("Timestamp")) {
                    long startTime = tgParam.getStartTime().getTime() / 1000;
                    long endTime = tgParam.getTriggerTime().getTime() / 1000;
                    doc.append(PARAMS_CM).append(TRANSFORM_QUOTES).append(String.format(replaceParam, startTime, endTime));
                } else {
                    SimpleDateFormat sdf = new SimpleDateFormat(replaceParamType);
                    String endTime = sdf.format(tgParam.getTriggerTime()).replaceAll(SPLIT_SPACE, PERCENT);
                    String startTime = sdf.format(tgParam.getStartTime()).replaceAll(SPLIT_SPACE, PERCENT);
                    doc.append(PARAMS_CM).append(TRANSFORM_QUOTES);
                    String temp = String.format(replaceParam, startTime, endTime);
                    JobLogger.log("------------------任务初始变量:" + temp);
                    //增加内置环境变量,tgParam.getTriggerTime() ,执行时间
                    temp = buildSysTimeParams(temp,replaceParam,replaceParamType,tgParam.getTriggerTime());
                    doc.append(temp);
                }
                //buildPartitionCM(doc, partitionStr);
                doc.append(TRANSFORM_QUOTES);

            } else if (IncrementTypeEnum.ID.getCode() == incrementType) {
                long startId = tgParam.getStartId();
                long endId = tgParam.getEndId();
                if (doc.length() > 0) doc.append(SPLIT_SPACE);
                doc.append(PARAMS_CM).append(TRANSFORM_QUOTES);
                String temp =String.format(replaceParam, startId, endId);
                if(StringUtils.isNotBlank(replaceParam)){//若配置系统变量 则进行解析，当未设定日期格式时,设置默认的日期格式
                    replaceParamType= StringUtils.isBlank(replaceParamType)?"yyy-MM-dd HH:mm:ss":replaceParamType;
                    temp = buildSysTimeParams(temp,replaceParam,replaceParamType,tgParam.getTriggerTime());
                }
                doc.append(temp);
                doc.append(TRANSFORM_QUOTES);
            }
        }

        if (incrementType != null && IncrementTypeEnum.PARTITION.getCode() == incrementType) {
            if (StringUtils.isNotBlank(partitionStr)) {
                List<String> partitionInfo = Arrays.asList(partitionStr.split(SPLIT_COMMA));
                if (doc.length() > 0) doc.append(SPLIT_SPACE);
                doc.append(PARAMS_CM).append(TRANSFORM_QUOTES).append(String.format(PARAMS_CM_V_PT, buildPartition(partitionInfo))).append(TRANSFORM_QUOTES);
            }
        }

        JobLogger.log("------------------Command parameters:" + doc);
        return doc.toString();
    }

    private static String buildPartition(List<String> partitionInfo) {
        String field = partitionInfo.get(0);
        int timeOffset = Integer.parseInt(partitionInfo.get(1));
        String timeFormat = partitionInfo.get(2);
        String partitionTime = DateUtil.format(DateUtil.addDays(new Date(), timeOffset), timeFormat);
        return field + Constants.EQUAL + partitionTime;
    }

    private static Date localDateTime2Date(LocalDateTime time) {
        return Date.from(time.toInstant(ZoneOffset.of("+8")));
    }

    private static LocalDateTime date2LocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

//    public static void main(String[] args) {
//        String s = " -DstartDate=SYS_STD_DATE-1D -DstartTime=SYS_STD_START_TIME-7d -DendTime=SYS_STD_END_TIME-7d -DlastTime='%s' -DcurrentTime='%s' ";
//        Date triggerDate = new Date();
//        String replaceParamType = "yyyy-MM-dd HH:mm:ss";
//        List<CustomTimeDomain> domains = analysis(s);
//        SimpleDateFormat sdf = new SimpleDateFormat(replaceParamType);
//        String temp = s;
//        if (!CollectionUtils.isEmpty(domains)) {
//            for (CustomTimeDomain domain : domains) {
//                Date date = calcTime(triggerDate, domain.getDomainVal());
//                //得到标准时间
//                if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_DATE_CATEGORY)) {
//                    String stdDate = sdf.format(date).replaceAll(SPLIT_SPACE, PERCENT);
//                    temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("='").concat(stdDate).concat("' "));
//                } else if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_STD_START_CATEGORY)) {
//                    String[] rgs = rangeTime(replaceParamType, date);
//                    temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("='").concat(rgs[0]).concat("' "));
//                } else if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_STD_END_CATEGORY)) {
//                    String[] rgs = rangeTime(replaceParamType, date);
//                    temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("='").concat(rgs[1]).concat("' "));
//                }
//
//            }
//        }
//        System.out.println(temp);
//    }

//    SYS_STD_START_TIME  年月日时分秒 开始日期
//     * SYS_STD_END_TIME    年月日时分秒 结束日期
//     * SYS_STD_DATE

    private static List<CustomTimeDomain> analysis(String replaceParam) {
//        Matcher matcher = VAR_PAT.matcher("-DstartTime=std_time-7d -DendTime=now-7d -DlastTime='%s' -DcurrentTime='%s' ");
        List<CustomTimeDomain> list = new ArrayList<>();
        Matcher matcher = VAR_PAT.matcher(replaceParam);
        while (matcher.find()) {
            CustomTimeDomain range = new CustomTimeDomain(matcher.group(0), matcher.group(1), matcher.group(2));
            TimeDomainVal domain = domainVal(range.getVal());
            if (domain != null) {
                range.setDomainVal(domain);
                list.add(range);
            }
        }
        return list;
    }

    private static TimeDomainVal domainVal(String val) {
        Matcher matcher = TIME_PAT.matcher(val);
        if (!matcher.find()) {
            return null;
        }
        TimeDomainVal domain = new TimeDomainVal();
        domain.setVar(matcher.group(1));
        domain.setUnit(matcher.group(3));
        domain.setVal(Integer.parseInt(matcher.group(2)));
        return domain;
    }

    private static Date calcTime(Date date, TimeDomainVal val) {
        LocalDateTime time = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        String model = val.getUnit().toUpperCase();
        if ("D".equals(model)) {
            return Date.from(time.plusDays(val.getVal()).toInstant(ZoneOffset.of("+8")));
        } else if ("H".equals(model)) {
            return Date.from(time.plusHours(val.getVal()).toInstant(ZoneOffset.of("+8")));
        } else if ("M".equals(model)) {
            return Date.from(time.plusMinutes(val.getVal()).toInstant(ZoneOffset.of("+8")));
        } else if ("S".equals(model)) {
            return Date.from(time.plusSeconds(val.getVal()).toInstant(ZoneOffset.of("+8")));
        } else {
            throw new IllegalArgumentException("未知时间模式:" + model);
        }
    }

    //日期的起始截至时间
    private static String[] rangeTime(String pat, LocalDateTime time) {
        LocalDateTime start = LocalDateTime.of(time.toLocalDate(), LocalTime.MIN);
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pat);
        LocalDateTime end = LocalDateTime.of(time.toLocalDate(), LocalTime.MAX);
        return new String[]{df.format(start), df.format(end)};
    }

    private static String[] rangeTime(String pat, Date date) {
        LocalDateTime time = date2LocalDateTime(date);
        return rangeTime(pat, time);
    }

    private static String buildSysTimeParams(String temp, String replaceParam, String replaceParamType, Date triggerTime) {
        List<CustomTimeDomain> domains = analysis(replaceParam);
        if (CollectionUtils.isEmpty(domains)) {
            return temp;
        }
        for (CustomTimeDomain domain : domains) {
            Date date = calcTime(triggerTime, domain.getDomainVal());
            //得到标准时间
            if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_DATE_CATEGORY)) {
                String stdDate = new SimpleDateFormat(replaceParamType.substring(0, replaceParamType.length() - 9)).format(date).replaceAll(SPLIT_SPACE, PERCENT);
                temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("=").concat(stdDate).concat(""));
                JobLogger.log("------------------解析标准日期(年月日):" + temp);
            } else if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_STD_START_CATEGORY)) {
                String[] rgs = rangeTime(replaceParamType, date);
                temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("='").concat(rgs[0].replaceAll(SPLIT_SPACE, PERCENT)).concat("'"));
                JobLogger.log("------------------解析开始日期(年月日时分秒):" + temp);
            } else if (domain.getDomainVal().getVar().equals(TimeDomainVal.SYS_STD_END_CATEGORY)) {
                String[] rgs = rangeTime(replaceParamType, date);
                temp = temp.replaceAll(domain.getPat(), "-D".concat(domain.getVar()).concat("='").concat(rgs[1].replaceAll(SPLIT_SPACE, PERCENT)).concat("'"));
                JobLogger.log("------------------解析结束日期(年月日时分秒):" + temp);
            }
        }
        return temp;
    }

    private void buildPartitionCM(StringBuilder doc, String partitionStr) {
        if (StringUtils.isNotBlank(partitionStr)) {
            doc.append(SPLIT_SPACE);
            List<String> partitionInfo = Arrays.asList(partitionStr.split(SPLIT_COMMA));
            doc.append(String.format(PARAMS_CM_V_PT, buildPartition(partitionInfo)));
        }
    }


}
